Skip to content

feat!: adds payref to ootle address - #1629

Merged
sdbondi merged 1 commit into
tari-project:developmentfrom
sdbondi:ootle-address-payref
Nov 4, 2025
Merged

feat!: adds payref to ootle address#1629
sdbondi merged 1 commit into
tari-project:developmentfrom
sdbondi:ootle-address-payref

Conversation

@sdbondi

@sdbondi sdbondi commented Nov 4, 2025

Copy link
Copy Markdown
Member

Description

feat!: adds payref to Ootleaddress
feat: updates JS Ootle Address de/encoder and web UI to generate payref address

Motivation and Context

Allows a payment ref. to be attached to an address. When provided to a compatible wallet, it will include the payment reference in the encrypted output memo for the payment.

How Has This Been Tested?

Manually, existing unit tests

What process can a PR reviewer use to test or verify this change?

Breaking Changes

  • None
  • Requires data directory to be deleted
  • Other - Please specify

Summary by CodeRabbit

  • New Features

    • Payment Reference (PayRef) support enabling users to embed and manage payment identifiers directly in wallet addresses.
    • QR code generation for addresses and payment reference sharing.
    • Enhanced memo types supporting payment references alongside fixed-size data fields.
  • UI Improvements

    • Redesigned transaction status indicators with improved visual clarity.
    • New payment reference management interface in account details for seamless creation and editing.

@coderabbitai

coderabbitai Bot commented Nov 4, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Extends the Tari wallet to support optional payment references (PayRef) embedded in addresses and memos. Changes include new Memo variants (U256 and PayRefAndBytes), OotleAddress serialization/deserialization to carry PayRef payloads, updated TypeScript bindings with PayRef type and address encoding helpers, new wallet daemon RPC endpoint for PayRef address generation, and web UI components for displaying and managing PayRef data through dialogs and QR codes.

Changes

Cohort / File(s) Summary
Memo and PayRef Core Types
bindings/src/types/Memo.ts, bindings/src/types/PayRef.ts, crates/wallet/crypto/src/memo.rs
Added new Memo variants: U256 (32-byte fixed) and PayRefAndBytes (length-prefixed pay_ref + message). Created PayRef type alias in bindings and new PayRef struct in crates with MAX_LEN=64, constructors, and serde support. Extended Memo with public accessors and constructors for new variants.
OotleAddress PayRef Support
crates/ootle_address/src/ootle_address.rs, crates/ootle_address/src/pay_ref.rs, crates/ootle_address/src/lib.rs
Added PayRef struct with bounded length validation (1–64 bytes) and hex serialization. Extended OotleAddress and RistrettoOotleAddress with optional pay_ref field, with_pay_ref/without_pay_ref mutators, and byte_length computation. Updated encoding/decoding to serialize/deserialize PayRef payloads; replaced AddressIncorrectLength error with AddressLengthTooShort/Long and BytesRemaining variants.
Wallet SDK and Stealth Transfer
applications/tari_walletd/src/handlers/accounts.rs, crates/wallet/sdk/src/apis/key_manager.rs, crates/wallet/sdk/tests/base_layer_compat.rs
Pre-computes stealth transfer outputs with pay_ref-aware memo construction before passing to StealthTransferParams. Key manager initializes OotleAddress pay_ref as None. Added test coverage for U256 memo round-trip encoding/decoding.
TypeScript Bindings and Client
bindings/src/index.ts, bindings/src/helpers/ootleAddress.ts, bindings/package.json, clients/javascript/wallet_daemon_client/src/index.ts, clients/wallet_daemon_client/Cargo.toml
Extended ootleAddress helpers to encode/decode PayRef in addresses with fallback UTF-8 decoding. Added decodeOotleAddressOrNull safe wrapper. Exported PayRef type in bindings index. New wallet daemon RPC method accountsGetPayRefAddress. Updated Cargo.toml ts feature to include downstream crate dependencies. Version bumped to reflect bindings changes.
Web UI Components – Status and Address
applications/tari_walletd/web_ui/src/components/StatusChip.tsx, applications/tari_walletd/web_ui/src/components/TransactionsStatusChip.tsx, applications/tari_walletd/web_ui/src/components/CopyAddress.tsx, applications/tari_walletd/web_ui/src/components/Memo.tsx
Refactored StatusChip to accept icon/color/children props instead of status-based logic; exported StatusChipIcons and StatusChipColors constants. Created new TransactionsStatusChip component for transaction status rendering. Narrowed CopyAddress display prop to string. Enhanced Memo component to decode and render PayRefAndBytes variant with copyable PayRef in blue StatusChip.
Web UI Routes and PayRef Dialog
applications/tari_walletd/web_ui/src/routes/AssetVault/Components/AccountDetails.tsx, applications/tari_walletd/web_ui/src/routes/Transactions/TransactionDetails.tsx, applications/tari_walletd/web_ui/src/routes/Transactions/Transactions.tsx
Added PayRef dialog in AccountDetails with TextField for entering payment reference, QR code display, and copy-to-clipboard. Dialog decodes address, sets PayRef, re-encodes, and updates display. Updated TransactionDetails and Transactions to use new TransactionsStatusChip instead of generic StatusChip.
Web UI Dependencies and Manifest
applications/tari_walletd/web_ui/package.json, clients/javascript/wallet_daemon_client/package.json
Added react-qr-code dependency (^2.0.18) for QR code rendering. Wallet daemon client version bumped from 1.11.0 to 1.11.1.
Type and Error Utilities
crates/template_lib_types/src/error.rs, crates/wallet/crypto/src/encrypted_data.rs
Added new constructor for InvalidByteLengthError. Updated memo decoding comment.
Tests
bindings/test/ootleAddress.test.ts
Renamed test helper makePayload to makeDecodedAddress. Added test for address encoding/decoding round-trip with PayRef. Adjusted tampering and invalid-input tests to use renamed helper.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • crates/ootle_address/src/ootle_address.rs: Dense encoding/decoding logic with PayRef length validation, new error variants (AddressLengthTooShort/Long, BytesRemaining, InvalidPayRefLengthSpecifier), and byte_length computation. Requires careful verification of length checks and serialization round-trips.
  • crates/wallet/crypto/src/memo.rs: Multiple new Memo variants and constructors with length-prefixed encoding for PayRefAndBytes; new helper functions split_len_prefixed and read_len_prefix for robust parsing.
  • crates/ootle_address/src/pay_ref.rs: PayRef struct with bounded length validation (1–64 bytes) and conditional serde implementations (hex vs. binary serialization).
  • bindings/src/helpers/ootleAddress.ts: Extended encoding/decoding with UTF-8 fallback for PayRef; ensure compatibility with both string and Uint8Array forms.
  • applications/tari_walletd/web_ui/src/components/StatusChip.tsx: Significant refactor from status-based to icon/color-based rendering; verify all consuming components (TransactionDetails, Transactions) correctly pass new props.
  • applications/tari_walletd/src/handlers/accounts.rs: Verify stealth transfer output pre-computation correctly embeds PayRef into memo and passes to StealthTransferParams without data loss.

Possibly related PRs

Suggested labels

P-acks_required, P-reviews_required

Poem

🐰 A burrow of references, now encoded with care,
PayRefs in addresses float through the air,
Memo variants multiply, U256 takes flight,
While QR codes blink in the wallet's new light,
From crypto to UI, the payment path gleams,
A rabbit's refactor fulfilling your dreams! 🥕✨

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat!: adds payref to ootle address' accurately describes the main feature addition: payment reference (payref) support for the Ootle address type, which is the core focus of this multi-file changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (7)
crates/template_lib_types/src/error.rs (1)

14-16: LGTM! Clean constructor implementation.

The constructor correctly initializes the error fields and follows idiomatic Rust patterns. Good use of pub(crate) fields with a public constructor for proper encapsulation.

Minor observation: Consider adding a getter method for the expected field (similar to actual_size() for the size field) to provide a consistent API for accessing error details.

applications/tari_walletd/src/handlers/accounts.rs (1)

1001-1002: Consider warning users when memo content is truncated.

The function new_pay_ref_and_bytes_truncate silently truncates the message bytes to fit alongside the pay_ref. Users may not realize their message was shortened, potentially leading to confusion or data loss.

Consider adding a log warning when truncation occurs:

+                let original_len = memo_bytes.len();
                 let memo = Memo::new_pay_ref_and_bytes_truncate(pay_ref, memo_bytes)
                     .ok_or_else(|| invalid_params("pay_ref", Some("pay_ref is too large to fit in memo")))?;
+                if memo.as_memo_bytes().map(|b| b.len()).unwrap_or(0) < original_len {
+                    warn!(
+                        target: LOG_TARGET,
+                        "⚠️ Memo content was truncated to fit pay_ref for transfer to address {}",
+                        transfer.destination_address
+                    );
+                }
applications/tari_walletd/web_ui/src/components/CopyAddress.tsx (1)

35-35: Potentially dead code: typeof object check after type narrowing.

Line 35 checks typeof display === "object", but display is now typed as string (line 29), making this branch unreachable. Consider removing the object check or clarifying the intent.

Apply this diff to simplify the logic:

-  const displayStr = display && (typeof display === "object" ? shortenSubstateId(display) : display);
+  const displayStr = display;
bindings/src/types/Memo.ts (1)

3-3: Update Memo component to explicitly handle U256 variant for consistent UX.

The PayRefAndBytes variant is properly handled. However, U256 currently falls through to the fallback JSON.stringify() case, which renders as {"U256":"..."} rather than a user-friendly display. Add explicit handling for U256 similar to the Bytes variant:

if ("U256" in memo) {
  return <span>{memo ? memo.U256 : "No Memo"}</span>;
}

Place this before the final fallback in applications/tari_walletd/web_ui/src/components/Memo.tsx.

applications/tari_walletd/web_ui/src/components/Memo.tsx (1)

6-6: Unused import detected.

The CopyAddress import on line 6 doesn't appear to be used in this file.

Apply this diff to remove the unused import:

-import CopyAddress from "@components/CopyAddress";
applications/tari_walletd/web_ui/src/routes/AssetVault/Components/AccountDetails.tsx (1)

132-132: Add input validation and meaningful form handling.

Line 132 has an empty onSubmit handler. If form submission isn't needed, consider removing the <form> wrapper or providing a meaningful handler (e.g., copying the address or closing the dialog).

Additionally, the payment reference TextField (lines 146-152) has no validation. Consider adding constraints to prevent excessively long payment references that could exceed encoding limits.

Apply this diff to add basic validation:

  const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>) => {
+   const payRefValue = event.target.value;
+   // Optionally add length validation here
+   if (payRefValue.length > 100) {  // Adjust limit as needed
+     return;
+   }
    const decoded = decodeOotleAddress(address);
-   decoded.payRef = event.target.value;
+   decoded.payRef = payRefValue;
    const addr = encodeOotleAddress(decoded);
    setCurrentAddress(addr);
  };

Also applies to: 146-152

applications/tari_walletd/web_ui/src/components/TransactionsStatusChip.tsx (1)

70-74: Remove unused variables.

Lines 71-72 declare leftColor and rightColor but these variables are never used in the code path when showTitle is false. These should be removed to clean up the code.

Apply this diff to remove the unused variables:

  if (!showTitle) {
-   let leftColor = colorList["Accepted"];
-   let rightColor = colorList["Rejected"];
-
    return <Avatar sx={{ bgcolor: bgColor, height: 22, width: 22 }}>{iconList[status]}</Avatar>;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e01b553 and 6ba30c7.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (26)
  • applications/tari_walletd/src/handlers/accounts.rs (1 hunks)
  • applications/tari_walletd/web_ui/package.json (1 hunks)
  • applications/tari_walletd/web_ui/src/components/CopyAddress.tsx (1 hunks)
  • applications/tari_walletd/web_ui/src/components/Memo.tsx (2 hunks)
  • applications/tari_walletd/web_ui/src/components/StatusChip.tsx (1 hunks)
  • applications/tari_walletd/web_ui/src/components/TransactionsStatusChip.tsx (1 hunks)
  • applications/tari_walletd/web_ui/src/routes/AssetVault/Components/AccountDetails.tsx (4 hunks)
  • applications/tari_walletd/web_ui/src/routes/Transactions/TransactionDetails.tsx (2 hunks)
  • applications/tari_walletd/web_ui/src/routes/Transactions/Transactions.tsx (2 hunks)
  • bindings/package.json (2 hunks)
  • bindings/src/helpers/ootleAddress.ts (5 hunks)
  • bindings/src/index.ts (1 hunks)
  • bindings/src/types/Memo.ts (1 hunks)
  • bindings/src/types/PayRef.ts (1 hunks)
  • bindings/test/ootleAddress.test.ts (4 hunks)
  • clients/javascript/wallet_daemon_client/package.json (1 hunks)
  • clients/javascript/wallet_daemon_client/src/index.ts (2 hunks)
  • clients/wallet_daemon_client/Cargo.toml (1 hunks)
  • crates/ootle_address/src/lib.rs (1 hunks)
  • crates/ootle_address/src/ootle_address.rs (18 hunks)
  • crates/ootle_address/src/pay_ref.rs (1 hunks)
  • crates/template_lib_types/src/error.rs (1 hunks)
  • crates/wallet/crypto/src/encrypted_data.rs (1 hunks)
  • crates/wallet/crypto/src/memo.rs (9 hunks)
  • crates/wallet/sdk/src/apis/key_manager.rs (1 hunks)
  • crates/wallet/sdk/tests/base_layer_compat.rs (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (14)
crates/ootle_address/src/lib.rs (1)
crates/ootle_address/src/ootle_address.rs (2)
  • pay_ref (74-76)
  • pay_ref (364-366)
bindings/src/types/Memo.ts (1)
applications/tari_walletd/web_ui/src/components/Memo.tsx (1)
  • Memo (13-49)
applications/tari_walletd/web_ui/src/routes/Transactions/TransactionDetails.tsx (1)
applications/tari_walletd/web_ui/src/components/TransactionsStatusChip.tsx (1)
  • TransactionsStatusChip (43-85)
applications/tari_walletd/web_ui/src/routes/Transactions/Transactions.tsx (1)
applications/tari_walletd/web_ui/src/components/TransactionsStatusChip.tsx (1)
  • TransactionsStatusChip (43-85)
applications/tari_walletd/web_ui/src/components/Memo.tsx (1)
applications/tari_walletd/web_ui/src/components/StatusChip.tsx (2)
  • StatusChip (55-94)
  • StatusChipColors (37-44)
crates/wallet/sdk/tests/base_layer_compat.rs (2)
crates/wallet/crypto/src/memo.rs (2)
  • new_u256 (54-57)
  • decode_from (197-241)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
applications/tari_walletd/src/handlers/accounts.rs (4)
crates/ootle_address/src/ootle_address.rs (2)
  • pay_ref (74-76)
  • pay_ref (364-366)
crates/wallet/crypto/src/memo.rs (2)
  • new_message (59-63)
  • new_pay_ref_and_bytes_truncate (81-91)
applications/tari_walletd/src/handlers/helpers.rs (1)
  • invalid_params (162-173)
bindings/src/types/wallet-daemon-client/TransferOutput.ts (1)
  • TransferOutput (6-24)
applications/tari_walletd/web_ui/src/components/TransactionsStatusChip.tsx (1)
bindings/src/types/TransactionStatus.ts (1)
  • TransactionStatus (3-11)
bindings/src/helpers/ootleAddress.ts (1)
bindings/src/types/Network.ts (1)
  • Network (6-6)
crates/ootle_address/src/pay_ref.rs (4)
bindings/src/types/PayRef.ts (1)
  • PayRef (3-3)
crates/wallet/crypto/src/memo.rs (1)
  • len (108-115)
crates/ootle_address/src/ootle_address.rs (5)
  • pay_ref (74-76)
  • pay_ref (364-366)
  • serialize (300-310)
  • new (31-38)
  • new (338-345)
crates/template_lib_types/src/hex.rs (2)
  • bytes_from_hex (20-31)
  • bytes_to_hex (33-37)
applications/tari_walletd/web_ui/src/routes/AssetVault/Components/AccountDetails.tsx (2)
bindings/src/types/OotleAddress.ts (1)
  • OotleAddress (3-3)
bindings/src/helpers/ootleAddress.ts (2)
  • decodeOotleAddress (37-90)
  • encodeOotleAddress (92-135)
crates/wallet/crypto/src/memo.rs (5)
crates/wallet/crypto/src/unblinded_statement.rs (2)
  • value (60-62)
  • memo (68-70)
applications/tari_walletd/web_ui/src/components/Memo.tsx (1)
  • Memo (13-49)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
crates/template_lib_types/src/max_bytes.rs (1)
  • new_checked (26-33)
crates/template_lib_types/src/max_string.rs (1)
  • new_checked (20-27)
bindings/test/ootleAddress.test.ts (1)
bindings/src/helpers/ootleAddress.ts (3)
  • encodeOotleAddress (92-135)
  • DecodedOotleAddress (12-17)
  • decodeOotleAddress (37-90)
crates/ootle_address/src/ootle_address.rs (2)
crates/ootle_address/src/hrp.rs (2)
  • hrp_from_network (14-23)
  • network_from_hrp (25-41)
crates/ootle_address/src/pay_ref.rs (3)
  • from_bytes (33-35)
  • len (21-23)
  • new_checked (12-19)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: check nightly
  • GitHub Check: clippy
  • GitHub Check: test
  • GitHub Check: check stable
  • GitHub Check: machete
  • GitHub Check: fmt
🔇 Additional comments (22)
bindings/package.json (2)

3-4: Version bump and package metadata look good.

The minor version bump to 1.20.1 is appropriate for adding new PayRef types and encoding/decoding helpers to the bindings. The description field provides useful context for consumers of the NPM package.


21-21: Minor: Build script whitespace cleanup.

Removed the extra space between pnpm run fmt && and tsc. This is a nice formatting improvement with no functional impact.

clients/javascript/wallet_daemon_client/package.json (1)

3-3: Verify version bump strategy aligns with breaking-change status.

The PR title indicates a breaking change ("feat!"), but the version is bumped from 1.11.0 to 1.11.1 (patch level). Per semantic versioning:

  • Patch bumps (1.11.1) are used for backward-compatible bug fixes or non-breaking changes.
  • Major bumps (2.0.0) are required for breaking changes.

However, the PR objectives state no breaking changes are included. There's a contradiction between the PR title ("feat!") and the stated breaking-change status. If the feature is truly non-breaking, the version should be 1.12.0 (minor bump) to reflect the new PayRef functionality. If it is breaking, the version should be 2.0.0.

Please clarify:

  1. Are there actual breaking changes in this PR, or should the commit title be "feat:" (without the "!")?
  2. Should the version be 1.12.0 (minor bump for new feature) or 2.0.0 (major bump for breaking changes)?
clients/wallet_daemon_client/Cargo.toml (1)

30-30: Change verified and approved.

All three dependent crates properly define the ts feature:

  • tari_ootle_wallet_sdk: ts = ["ts-rs", "tari_ootle_address/ts"]
  • tari_ootle_common_types: ts = ["ts-rs"]
  • tari_ootle_address: ts = ["ts-rs"]

The feature propagation in the file is syntactically correct and all referenced features exist.

clients/javascript/wallet_daemon_client/src/index.ts (2)

20-20: LGTM! Import additions are clean.

The new types AccountsGetPayRefAddressRequest and AccountsGetPayRefAddressResponse are properly imported and grouped logically with related account types.


180-182: Code review verified and approved.

The types AccountsGetPayRefAddressRequest and AccountsGetPayRefAddressResponse are correctly imported from @tari-project/typescript-bindings (line 15 of imports). The method implementation follows the established pattern used by all other RPC wrapper methods in the class, with consistent naming conventions for both the method name (camelCase) and RPC endpoint (snake_case). No issues found.

applications/tari_walletd/src/handlers/accounts.rs (1)

981-1026: Good refactoring to pre-process outputs.

The change to collect and transform outputs before passing them to StealthTransferParams is a solid improvement. Pre-processing outputs makes the pay_ref embedding logic clearer and keeps the params construction clean.

crates/wallet/crypto/src/encrypted_data.rs (1)

219-219: LGTM - Minor grammatical improvement.

The comment update improves readability without any functional changes.

applications/tari_walletd/web_ui/package.json (1)

28-28: LGTM - QR code dependency added for PayRef feature.

The addition of react-qr-code supports the new PayRef UI functionality for displaying addresses as QR codes.

crates/ootle_address/src/pay_ref.rs (2)

60-84: LGTM - Serde implementation correctly validates PayRef constraints.

The serialization uses hex for human-readable formats and raw bytes otherwise. Deserialization properly validates length constraints through new_checked, preventing invalid PayRef instances.


12-19: Verify PayRef validation aligns with consumer expectations.

The validation requires PayRef to be non-empty and ≤ 64 bytes. Ensure this constraint is documented and enforced consistently across all creation paths (including deserialization and UI inputs).

Run the following script to check for any PayRef creation that might bypass validation:

bindings/src/index.ts (1)

84-84: LGTM - PayRef type properly exported.

The export follows the existing pattern and makes the PayRef type available to TypeScript consumers.

crates/wallet/sdk/src/apis/key_manager.rs (1)

195-211: LGTM - PayRef correctly initialized as None for default addresses.

The pay_ref: None initialization is appropriate for the derive_account_address method, which generates standard addresses without payment references. This aligns with the optional PayRef support in OotleAddress.

crates/ootle_address/src/lib.rs (1)

6-9: LGTM - PayRef module properly declared and exported.

The module declaration and re-export follow the existing pattern and make PayRef publicly available from the crate root.

applications/tari_walletd/web_ui/src/routes/Transactions/TransactionDetails.tsx (1)

51-51: LGTM! Component refactor is consistent.

The replacement of StatusChip with TransactionsStatusChip is clean and maintains the same props interface.

Also applies to: 179-179

bindings/src/types/PayRef.ts (1)

1-3: LGTM! Simple and appropriate type definition.

The PayRef type alias as a string provides flexibility for payment reference formats.

applications/tari_walletd/web_ui/src/components/Memo.tsx (1)

51-58: LGTM! Clean helper function.

The tryDecodeUtf8 helper provides a safe way to attempt UTF-8 decoding with appropriate fallback handling.

bindings/test/ootleAddress.test.ts (1)

8-14: LGTM! Test improvements enhance coverage.

The helper rename to makeDecodedAddress is more descriptive, and the new test case provides thorough coverage of the payRef encoding/decoding functionality, including verification that addresses with and without payRef differ.

Also applies to: 31-50

crates/wallet/sdk/tests/base_layer_compat.rs (1)

20-27: LGTM! Solid test coverage for U256 memos.

The new test path correctly verifies round-trip serialization and deserialization of 256-bit memo fields, ensuring compatibility between MemoField::new_u256 and Memo::new_u256.

applications/tari_walletd/web_ui/src/routes/Transactions/Transactions.tsx (1)

40-40: LGTM! Consistent component refactor.

The replacement of StatusChip with TransactionsStatusChip maintains the same props interface (status and showTitle) and is consistent with the refactor in TransactionDetails.tsx.

Also applies to: 103-103

applications/tari_walletd/web_ui/src/routes/AssetVault/Components/AccountDetails.tsx (1)

56-56: LGTM! Clean dialog integration.

The PayRef dialog state management and trigger button are well-implemented with appropriate tooltips for user guidance.

Also applies to: 69-69, 92-96

applications/tari_walletd/web_ui/src/components/TransactionsStatusChip.tsx (1)

43-84: LGTM! Well-structured status chip component.

The TransactionsStatusChip component provides clear visual feedback for transaction statuses with appropriate icons and colors. The special handling for OnlyFeeAccepted with a gradient background is a nice touch for representing the mixed state.

Comment on lines +1001 to +1002
let memo = Memo::new_pay_ref_and_bytes_truncate(pay_ref, memo_bytes)
.expect("payref + truncated message fits in memo");

@coderabbitai coderabbitai Bot Nov 4, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Handle potential None from new_pay_ref_and_bytes_truncate gracefully.

The expect() on line 1002 will panic if new_pay_ref_and_bytes_truncate returns None. According to the implementation in memo.rs, this function returns None when the pay_ref itself is too large to fit in a memo (exceeds MAX_BYTES_LENGTH - 1). This should be handled with a proper error instead of panicking.

Apply this diff to handle the error gracefully:

-                let memo = Memo::new_pay_ref_and_bytes_truncate(pay_ref, memo_bytes)
-                    .expect("payref + truncated message fits in memo");
+                let memo = Memo::new_pay_ref_and_bytes_truncate(pay_ref, memo_bytes)
+                    .ok_or_else(|| invalid_params("pay_ref", Some("pay_ref is too large to fit in memo")))?;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let memo = Memo::new_pay_ref_and_bytes_truncate(pay_ref, memo_bytes)
.expect("payref + truncated message fits in memo");
let memo = Memo::new_pay_ref_and_bytes_truncate(pay_ref, memo_bytes)
.ok_or_else(|| invalid_params("pay_ref", Some("pay_ref is too large to fit in memo")))?;
🤖 Prompt for AI Agents
In applications/tari_walletd/src/handlers/accounts.rs around lines 1001-1002,
replace the unconditional expect on Memo::new_pay_ref_and_bytes_truncate (which
can return None when pay_ref is too large) with a graceful check that handles
the None case and returns an appropriate error instead of panicking; detect the
None result, construct and return a meaningful error (e.g., a
BadRequest/InvalidArgument or domain-specific error indicating the pay_ref is
too large to fit in a memo), or propagate it via the function's Result, ensuring
no panic path remains.

@sdbondi sdbondi Nov 4, 2025

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because payref has maximum of 64 bytes and the rest of truncated, returning None is not possible (or if it does, it's a bug)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the implementation of new_pay_ref_and_bytes_truncate in memo.rs
echo "=== Checking new_pay_ref_and_bytes_truncate implementation ==="
ast-grep --pattern $'fn new_pay_ref_and_bytes_truncate($$$) -> Option<Self> {
  $$$
}'

echo -e "\n=== Checking MAX_BYTES_LENGTH constant in memo.rs ==="
rg -n "MAX_BYTES_LENGTH" crates/wallet/crypto/src/memo.rs -A 2 -B 2

echo -e "\n=== Checking PayRef::MAX_LEN ==="
rg -n "MAX_LEN" crates/ootle_address/src/pay_ref.rs -A 1 -B 1

Length of output: 4948


@sdbondi You're absolutely correct! I've verified the implementation and your reasoning is sound:

  • PayRef::MAX_LEN = 64 bytes (enforced in PayRef::new_checked)
  • Memo::MAX_BYTES_LENGTH = 253 bytes
  • new_pay_ref_and_bytes_truncate only returns None if pay_ref.len() > MAX_BYTES_LENGTH - 1 (i.e., > 252 bytes)
  • Since 64 < 252, a valid PayRef will never cause this function to return None

The expect() is safe here because the payref is validated during address decoding, ensuring it never exceeds 64 bytes. The message portion is truncated as needed to fit in the remaining space (253 - 1 byte length prefix - payref length).

The only minor note is that the expect message "payref + truncated message fits in memo" is slightly imprecise—it's really about the payref size being valid, but that's a trivial naming detail. The logic is correct.


🧬 Code Graph Analysis Results

[relevant_code_snippets]

  • File: crates/wallet/crypto/src/memo.rs
    Lines: 81-91
    Content:

    pub fn new_pay_ref_and_bytes_truncate<P: AsRef<[u8]>, B: AsRef<[u8]>>(pay_ref: P, msg_bytes: B) -> Option<Self> {
        let pr = pay_ref.as_ref();
        let available_len = (Self::MAX_BYTES_LENGTH - 1).checked_sub(pr.len())?; // -1 for length prefix byte
        let mb = msg_bytes.as_ref();
        let mb = if mb.len() > available_len {
            &mb[..available_len]
        } else {
            mb
        };
        Self::new_pay_ref_and_bytes(pay_ref, mb)
    }
    
  • File: bindings/src/types/wallet-daemon-client/TransferOutput.ts
    Lines: 6-24
    Content:

    export type TransferOutput = {
      /**
       * Destination address used to derive the UTXO encryption keys, owner signature and the account in which to
       * deposit revealed funds
       */
      address: OotleAddress;
      /**
       * Amount to spend to a revealed output
       */
      revealed_amount: Amount;
      /**
       * Amount to spend to a blinded output
       */
      blinded_amount: Amount;
      /**
       * Optional memo to include a memo in the output. This memo is encrypted and can only be read by the recipient.
       */
      memo: Memo | null;
    };
    
  • File: bindings/src/types/wallet-daemon-client/Memo.ts
    Lines: 3-3
    Content:

    export type Memo = { U256: string } | { Message: string } | { Bytes: string } | { PayRefAndBytes: string };
    

✏️ Learnings added
Learnt from: sdbondi
Repo: tari-project/tari-ootle PR: 1629
File: applications/tari_walletd/src/handlers/accounts.rs:1001-1002
Timestamp: 2025-11-04T10:10:24.249Z
Learning: In applications/tari_walletd/src/handlers/accounts.rs, the expect() on Memo::new_pay_ref_and_bytes_truncate at line 1002 is safe and intentional. PayRef is validated to be at most 64 bytes during address decoding (PayRef::MAX_LEN = 64), and the function only returns None if payref exceeds 252 bytes (Memo::MAX_BYTES_LENGTH - 1). Since 64 < 252, None is impossible with a valid PayRef.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment thread applications/tari_walletd/web_ui/src/components/CopyAddress.tsx
Comment thread applications/tari_walletd/web_ui/src/components/Memo.tsx
Comment thread bindings/src/helpers/ootleAddress.ts
Comment thread bindings/src/helpers/ootleAddress.ts
Comment thread crates/ootle_address/src/pay_ref.rs
@sdbondi
sdbondi merged commit 8a212ec into tari-project:development Nov 4, 2025
12 checks passed
@sdbondi
sdbondi deleted the ootle-address-payref branch November 4, 2025 10:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants